/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit.policy;

import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.uml2.uml.UMLPackage;

import com.cea.papyrus.core.dnd.ObjectTransfer;
import com.cea.papyrus.core.editpart.GraphEdgeEditPart;
import com.cea.papyrus.core.editpart.GraphNodeEditPart;
import com.cea.papyrus.core.editpart.LayoutEditPolicy;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagram.sequence.sedi.edit.AbstractEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.AbstractLinkEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.CFEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.ContinuationEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.EOEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.GuardEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.IOEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.LifelineEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.StateInvariantEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.TerminateEditPart;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.ActionExecutionSpecificationCreateHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.BehaviorExecutionSpecificationCreateHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.CombinedFragmentCreateHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.ContinuationCreateHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.ContinuationLayoutHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.ExecutionSpecificationLayoutHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.GuardCreateHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.InteractionAddCFHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.InteractionAddGuardHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.InteractionUseCreationHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.InteractionUseLayoutHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.LifelineCreationHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.LifelineLayoutHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.StateInvariantCreateHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.StateInvariantLayoutHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.StopCreateHandler;
import com.cea.papyrus.diagram.sequence.sedi.edit.handlers.StopLayoutHandler;
import com.cea.papyrus.diagram.sequence.sedi.model.command.diagram.SetBoundsHintCommand;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * Layout policy that is installed on Frame edit parts.
 * 
 * @author Andreas Limyr
 * @author frankd@ifi.uio.no
 */
public class FrameLayoutPolicy extends LayoutEditPolicy {

    public FrameLayoutPolicy() {
		super(new XYLayout());
	}
    
    @Override
    protected Command createAddCommand(EditPart child, Object constraint) {
        Command[] cmds = null;
        if (child instanceof CFEditPart) {
            // CFs expect diagram coordinates
            Rectangle r = (Rectangle) constraint;
            ((GraphicalEditPart) getHost()).getFigure().translateToParent(r);

            cmds = new InteractionAddCFHandler(this).getAddCommands((GraphicalEditPart) child, r);
        } else if (child instanceof GuardEditPart) {
            // guards expect absolute coordinates
            Rectangle r = (Rectangle) constraint;
            ((GraphicalEditPart) getHost()).getFigure().translateToParent(r);
            ((GraphicalEditPart) getHost()).getFigure().translateToAbsolute(r);

            cmds =
                    new InteractionAddGuardHandler(this).getAddCommands((GraphNode) getHost()
                            .getModel(), (GraphicalEditPart) child, r);
        }

        if (cmds != null) {
            CompoundCommand ccmd = new CompoundCommand();
            for (Command c : cmds)
                ccmd.add(c);
            return ccmd;
        }
        return null;
    }

    @Override
    protected Command createChangeConstraintCommand(final EditPart childEP, final Object constraint) {

        CompoundCommand ccmd = new CompoundCommand();

        Command[] cmds = null;
        if (childEP instanceof LifelineEditPart) {
            cmds = new LifelineLayoutHandler().getMoveCommands(childEP, (Rectangle) constraint);
            ccmd.setLabel("Move/resize Lifeline");
            
        } else if (childEP instanceof IOEditPart) {
            cmds =
                    new InteractionUseLayoutHandler().getMoveCommands(childEP,
                            (Rectangle) constraint);
            ccmd.setLabel("Move/resize Interaction Use");
            
        } else if (childEP instanceof StateInvariantEditPart) {
        	StateInvariantLayoutHandler handler = new StateInvariantLayoutHandler();
        	handler.setDetachedFromLifeline(true);
            cmds = 
            	handler.getMoveCommands(
            		childEP,
            		(Rectangle) constraint);
            ccmd.setLabel("Move State Invariant");
            
        } else if (childEP instanceof TerminateEditPart) {
        	StopLayoutHandler stopLayoutHandler = new StopLayoutHandler();
        	stopLayoutHandler.setDetachedFromLifeline(true);
            cmds = stopLayoutHandler.getMoveCommands(
            		(TerminateEditPart) childEP, 
            		(Rectangle) constraint);
            ccmd.setLabel("Move Stop");
            
        } else if (childEP instanceof ContinuationEditPart) {
            cmds = new ContinuationLayoutHandler().getMoveCommands(childEP, (Rectangle) constraint);
            ccmd.setLabel("Move/resize Continuation");
            
        } else if (childEP instanceof EOEditPart) {
        	ExecutionSpecificationLayoutHandler handler = new ExecutionSpecificationLayoutHandler();
        	handler.setDetachedFromLifeline(true);
            cmds =
            	handler.getMoveCommands(
            			childEP,
            			(Rectangle) constraint);
            ccmd.setLabel("Move/resize Execution Occurrence");
            
        }

        if (cmds != null) {
            for (Command c : cmds)
                ccmd.add(c);
            return ccmd;
        }
        return null;
    }

    @Override
    protected Command getCreateCommand(final CreateRequest request) {

		// This is a drag'n'drop
		if (request.getExtendedData().get(ObjectTransfer.UML_TRANSFER_OBJECT)!=null) {
			return super.getCreateCommand(request);
		}
        
		// This is an element creation
        EClass umlClass = null;
        if (request.getNewObject() != null) {
        	umlClass = (EClass) request.getNewObject();
        }
        
        // Comment or Constraint creation uses ClassDiagram command
        if ((umlClass == null) && (request.getNewObjectType() != null)) {
        	return super.getCreateCommand(request);
        }

        final CompoundCommand ccmd = new CompoundCommand();
        ccmd.setLabel("Create");

        // All children have bounds
        final Rectangle constraint = (Rectangle) getConstraintFor(request);
        // final Rectangle bounds = new Rectangle(constraint.x, constraint.y, 0, 0);
        final SetBoundsHintCommand hintCmd = new SetBoundsHintCommand();
        hintCmd.setHint(constraint);
        ccmd.add(hintCmd);

        boolean ignoreSizeHint = true;

        // Delegate the rest of the creation process to the creation handlers
        Command[] cmds = null;
        if (umlClass == UMLPackage.eINSTANCE.getLifeline())
            cmds = new LifelineCreationHandler(this).getCreateCommands(request);
        else if (umlClass == UMLPackage.eINSTANCE.getInteractionUse()) {
            ignoreSizeHint = false;
            cmds = new InteractionUseCreationHandler(this).getCreateCommands(request);
        } else if (umlClass == UMLPackage.eINSTANCE.getStateInvariant())
            cmds = new StateInvariantCreateHandler(this).getCreateCommands(request);
        else if (umlClass == UMLPackage.eINSTANCE.getDestructionEvent())
            cmds = new StopCreateHandler(this).getCreateCommands(request);
        else if (umlClass == UMLPackage.eINSTANCE.getCombinedFragment()) {
            ignoreSizeHint = false;
            cmds =
                    new CombinedFragmentCreateHandler(this).getCreateCommands(request, constraint
                            .getLocation());
        } else if (umlClass == UMLPackage.eINSTANCE.getContinuation()) {
            ignoreSizeHint = false;
            cmds = new ContinuationCreateHandler(this).getCreateCommands(request);
        } else if (umlClass == UMLPackage.eINSTANCE.getInteractionConstraint())
            cmds =
                    new GuardCreateHandler(this).getCreateCommands(request, constraint
                            .getLocation());
        else if (umlClass == UMLPackage.eINSTANCE.getBehaviorExecutionSpecification())
            cmds = new BehaviorExecutionSpecificationCreateHandler(this).getCreateCommands(request);
        else if (umlClass == UMLPackage.eINSTANCE.getActionExecutionSpecification()) {
            cmds = new ActionExecutionSpecificationCreateHandler(this).getCreateCommands(request);
        }

        if (ignoreSizeHint) {
            constraint.setSize(0, 0);
            hintCmd.setHint(constraint);
        }

        if (cmds != null) {
            for (Command cmd : cmds)
                ccmd.add(cmd);
            return ccmd;
        }
        return null;
    }

    // The LayoutEditPolicy implementation always installs a PRIMARY_DRAG_ROLE
    // edit policy on children, overriding any that is already installed.
    // We actually really want to keep the one we have installed on the editpart,
    // because resizing and movement policies differs for some editparts.
    // EOs, for example, can only resize downwards, and several editparts
    // snap to other objects in different ways.
    @Override
    protected void decorateChild(EditPart child) {
        if (child.getEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE) == null)
            super.decorateChild(child);
    }

    /*
     * protected EditPolicy createChildEditPolicy(EditPart child) { return new
     * ResizableEditPolicy(); }
     */

    @Override
    protected Command getDeleteDependantCommand(Request arg0) {
        return null;
    }

	@Override
	protected Command createChangeConstraintCommand(
			ChangeBoundsRequest request, EditPart child, Object constraint) {

		if ((child instanceof GraphNodeEditPart)
				|| (child instanceof GraphEdgeEditPart)) {
			// EditPart from Papyrus
			return super.createChangeConstraintCommand(request, child, constraint);
			
		} else if ((child instanceof AbstractLinkEditPart<?>)
				|| (child instanceof AbstractEditPart<?>)) {
			// EditPart from SeDi
			return createChangeConstraintCommand(child, constraint);
			
		} else {
			Debug.log(new IllegalArgumentException("Unexpected EditPart type : "+child));
			return null;
		}
	}
}
